﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using System.Xml;
using System.Xml.Linq;
using System.Xml.XPath;
using System.IO;

namespace CrmWorkflowDependencies.Dependencies
{
    public static class XamlExtensions
    {
        static XamlExtensions()
        {
            namespaceManager = new XmlNamespaceManager(new NameTable());
            namespaceManager.AddNamespace("mxswa", "clr-namespace:Microsoft.Xrm.Sdk.Workflow.Activities;assembly=Microsoft.Xrm.Sdk.Workflow, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
            namespaceManager.AddNamespace("mxs", "clr-namespace:Microsoft.Xrm.Sdk;assembly=Microsoft.Xrm.Sdk, Version=5.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35");
        }

        static XmlNamespaceManager namespaceManager;

        private delegate VertexProperty BuidVertex(XElement e);        

        private static VertexProperty[] XamlRetrieve(this Vertex workflow, string xpath, BuidVertex buildVertexFunc)
        {
            var list = new List<VertexProperty>();

            DependenciesException.ThrowIf(componenttype.Workflow != workflow.Type, "XamlRetrieve vertext must be a workflow, but {0}", workflow.Type);
            DependenciesException.ThrowIf(!workflow.Properties.ContainsKey("xaml"), "in the workflow must be a 'xaml' field");
            string xaml = (string)workflow.Properties["xaml"];
            DependenciesException.ThrowIf(string.IsNullOrEmpty(xaml), "xaml is null");

            var doc = XDocument.Parse(xaml);

            foreach (XElement e in doc.XPathSelectElements(xpath, namespaceManager))
            {
                list.Add(buildVertexFunc(e));
            }

            return list.ToArray();
        }

        public static VertexProperty[] XamlRetrieveSetEntityProperty(this Vertex workflow)
        {
            return workflow.XamlRetrieve("//mxswa:SetEntityProperty[@Attribute and @EntityName]", delegate(XElement e) {
                XAttribute attr = e.Attribute(XName.Get("Attribute"));
                DependenciesException.ThrowIf(attr == null, "Attribute in {0}", e.Name);
                DependenciesException.ThrowIf(string.IsNullOrEmpty(attr.Value), "attr.Value is empty");

                XAttribute etn = e.Attribute(XName.Get("EntityName"));
                DependenciesException.ThrowIf(etn == null, "in {0} must be EntityName attribute", e.Name);
                DependenciesException.ThrowIf(string.IsNullOrEmpty(etn.Value), "etn.Value is empty");

                return new VertexProperty(new Vertex(etn.Value) { Type = componenttype.Entity }, attr.Value) { Type = componenttype.ManagedProperty };
            });
        }
        
        public static VertexProperty[] XamlRetrieveSetState(this Vertex workflow)
        {
            return workflow.XamlRetrieve("//mxswa:SetState[@EntityName]", delegate(XElement e)
            {                
                XAttribute etn = e.Attribute(XName.Get("EntityName"));
                DependenciesException.ThrowIf(etn == null, "in {0} must be EntityName attribute", e.Name);
                DependenciesException.ThrowIf(string.IsNullOrEmpty(etn.Value), "etn.Value is empty");

                XElement status = e.XPathSelectElement("mxswa:SetState.Status//mxs:OptionSetValue[@Value]", namespaceManager);
                DependenciesException.ThrowIf(status == null, "mxswa:SetState.Status//mxs:OptionSetValue[@Value] must be found");
                XAttribute avalue = status.Attribute(XName.Get("Value"));
                DependenciesException.ThrowIf(avalue == null, "value not found");
                DependenciesException.ThrowIf(string.IsNullOrEmpty(avalue.Value), "value is empty");

                var entity = new Vertex(etn.Value) { Type = componenttype.Entity };
                //var attribute = new VertexProperty(entity, "Statuscode") { Type = componenttype.Attribute };

                //return new VertexProperty(attribute, avalue.Value) { Type = componenttype.AttributePicklistValue };
                return new VertexProperty(entity, avalue.Value) { Type = componenttype.AttributePicklistValue };
            });
        }

        public static VertexProperty[] XamlRetrieveNonMicrosoftAssemblyQN(this Vertex workflow)
        {
            /// <mxswa:ActivityReference AssemblyQualifiedName="Cryptopro.Crm.WF.Integration.OrderTo1C, Cryptopro.Crm.WF.Integration, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" DisplayName="CustomActivityStep1">

            return workflow.XamlRetrieve("//mxswa:ActivityReference[not(starts-with(@AssemblyQualifiedName, 'Microsoft'))]", delegate(XElement e)
            {
                XAttribute aqn = e.Attribute(XName.Get("AssemblyQualifiedName"));
                DependenciesException.ThrowIf(aqn == null, "AssemblyQualifiedName is null");
                DependenciesException.ThrowIf(string.IsNullOrEmpty(aqn.Value), "AssemblyQualifiedName value is empty");

                DependenciesException.ThrowIf(!aqn.Value.Contains(','), "AssemblyQualifiedName must contains a ',' characters"); /// разделение typename и assemblyname

                string[] tncomponents = aqn.Value.Split(new[] { ", " }, StringSplitOptions.RemoveEmptyEntries);
                DependenciesException.ThrowIf(tncomponents.Length <= 2, "AssemblyQualifiedName");

                string tn = tncomponents[0];
                string an = tncomponents[1];

                var assembly = new Vertex(an) { Type = componenttype.PluginAssembly };

                return new VertexProperty(assembly, tn) { Type = componenttype.PluginType };
            });
        }
    }
}
